package com.xuecheng.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.base.exception.XueChengPlusException;
import com.xuecheng.base.utils.IdWorkerUtils;
import com.xuecheng.base.utils.QRCodeUtil;
import com.xuecheng.messagesdk.model.po.MqMessage;
import com.xuecheng.messagesdk.service.MqMessageService;
import com.xuecheng.order.config.AlipayConfig;
import com.xuecheng.order.config.PayNotifyConfig;
import com.xuecheng.order.mapper.OrdersMapper;
import com.xuecheng.order.model.dto.AddOrderDto;
import com.xuecheng.order.model.dto.PayRecordDto;
import com.xuecheng.order.model.dto.PayStatusDto;
import com.xuecheng.order.model.po.Orders;
import com.xuecheng.order.model.po.OrdersGoods;
import com.xuecheng.order.model.po.PayRecord;
import com.xuecheng.order.service.OrdersGoodsService;
import com.xuecheng.order.service.OrdersService;
import com.xuecheng.order.service.PayRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders>
    implements OrdersService {
    @Value("${pay.alipay.APP_ID}")
    public String appid;

    @Value("${pay.alipay.APP_PRIVATE_KEY}")
    public String appPrivateKey;

    @Value("${pay.alipay.ALIPAY_PUBLIC_KEY}")
    public String alipayPublicKey;

    @Value("${pay.qrcodeurl}")
    String qrcodeUrl;

    @Autowired
    OrdersGoodsService ordersGoodsService;

    @Autowired
    PayRecordService payRecordService;

    @Autowired
    OrdersService currentProxy;

    @Autowired
    MqMessageService mqMessageService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    /**
     * 创建订单并返回支付记录和二维码
     * @param addDto
     * @param userId
     * @return
     */
    @Override
    @Transactional
    public PayRecordDto createOrder(AddOrderDto addDto, Long userId) {
        Orders order = saveOrders(userId,addDto);
        PayRecord record = createPayRecord(order);
        String qrcode = null;
        QRCodeUtil qrCodeUtil = new QRCodeUtil();
        String content = String.format(qrcodeUrl,record.getPayNo());
        try {
            qrcode = qrCodeUtil.createQRCode(content,200,200);
        } catch (IOException e) {
            XueChengPlusException.cast("生成二维码出错");
        }
        PayRecordDto dto = new PayRecordDto();
        BeanUtils.copyProperties(record,dto);
        dto.setQrcode(qrcode);
        return dto;
    }

    /**
     * 创建支付记录
     * @param order
     * @return
     */
    public PayRecord createPayRecord(Orders order) {
        if(order == null){
            XueChengPlusException.cast("订单不存在");
        }
        if(order.getStatus().equals("600002")){
            XueChengPlusException.cast("订单已支付");
        }
        PayRecord record = new PayRecord();
        record.setOrderId(order.getId());
        record.setOrderName(order.getOrderName());
        record.setTotalPrice(order.getTotalPrice());
        record.setPayNo(IdWorkerUtils.getInstance().nextId());
        record.setCurrency("CNY");
        record.setStatus("601001");//未支付
        record.setUserId(order.getUserId());
        payRecordService.save(record);
        return record;
    }

    /**
     * 创建订单和商品订单明细
     * @param userId
     * @param addDto
     * @return
     */
    public Orders saveOrders(Long userId,AddOrderDto addDto){
        Orders order = getOrderByBusinessId(addDto.getOutBusinessId());
        if(order != null){
            return order;
        }
        order = new Orders();
        BeanUtils.copyProperties(addDto,order);
        order.setStatus("600001");
        order.setUserId(userId);
        OrdersGoods orderGoods = new OrdersGoods();
        order.setId(IdWorkerUtils.getInstance().nextId());
        save(order);
        List<OrdersGoods> goods = JSON.parseArray(order.getOrderDetail(),
                OrdersGoods.class);
        for(OrdersGoods good : goods){
            good.setOrderId(order.getId());
        }
        ordersGoodsService.saveBatch(goods);
        return order;
    }

    /**
     * 根据关联业务id获取订单信息
     * @param businessId
     * @return
     */
    public Orders getOrderByBusinessId(Long businessId){
        return getOne(new LambdaQueryWrapper<Orders>()
                .eq(Orders::getOutBusinessId,businessId));
    }

    /**
     * 根据交易号查询支付记录
     * @param payNo
     * @return
     */
    @Override
    public PayRecord getPayRecordByPayNo(Long payNo) {
        return payRecordService.getOne(new LambdaQueryWrapper<PayRecord>()
                .eq(PayRecord::getPayNo,payNo));
    }

    /**
     * 根据支付记录号查询支付结果
     * @param payNo
     * @return
     */
    @Override
    public PayRecordDto queryPayResult(Long payNo) {
        PayRecordDto dto = new PayRecordDto();
        PayRecord record = getPayRecordByPayNo(payNo);
        if(record == null){
            XueChengPlusException.cast("当前支付记录不存在，请重新点击二维码");
        }
        if("601002".equals(record.getStatus())){
            BeanUtils.copyProperties(record,dto);
            return dto;
        }
        //从支付宝查询对应的支付结果
        PayStatusDto statusDto = queryPayResultFromAlipay(payNo);
        //保存支付结果
        currentProxy.saveAliPayStatus(statusDto);
        record = getPayRecordByPayNo(payNo);
        BeanUtils.copyProperties(record,dto);
        return dto;
    }

    /**
     * 根据支付记录号到支付宝查询支付结果
     * @param payNo
     * @return
     */
    private PayStatusDto queryPayResultFromAlipay(Long payNo) {
        //请求支付宝查询支付结果
        AlipayClient client  = new DefaultAlipayClient(AlipayConfig.URL,appid,appPrivateKey,
                AlipayConfig.FORMAT,AlipayConfig.CHARSET,alipayPublicKey,AlipayConfig.SIGNTYPE);
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no",payNo);
        request.setBizContent(bizContent.toString());
        AlipayTradeQueryResponse response = null;
        try{
            response = client.execute(request);
            if(!response.isSuccess()){
                XueChengPlusException.cast("请求支付查询查询失败");
            }
        }catch(AlipayApiException e){
            log.error("请求支付宝查询支付结果异常:{}", e.toString(), e);
            XueChengPlusException.cast("请求支付查询查询失败");
        }
        //获取支付结果
        String resultJson = response.getBody();
        //转map
        Map resultMap = JSON.parseObject(resultJson,Map.class);
        Map alipay_trade_query_response = (Map) resultMap.get("alipay_trade_query_response");
        //支付结果
        String trade_status = (String) alipay_trade_query_response.get("trade_status");
        String total_amount = (String) alipay_trade_query_response.get("total_amount");
        String trade_no = (String) alipay_trade_query_response.get("trade_no");
        PayStatusDto dto = new PayStatusDto();
        dto.setApp_id(appid);
        dto.setTotal_amount(total_amount);
        dto.setOut_trade_no(payNo.toString());
        dto.setTrade_no(trade_no);
        dto.setTrade_status(trade_status);
        return dto;
    }

    /**
     * 保存支付宝支付结果
     * @param payStatusDto  支付结果信息
     */
    @Transactional
    @Override
    public void saveAliPayStatus(PayStatusDto payStatusDto) {
        Long payNo = Long.parseLong(payStatusDto.getOut_trade_no());
        PayRecord record = getPayRecordByPayNo(payNo);
        if(record == null){
            XueChengPlusException.cast("支付记录找不到");
        }
        String status = payStatusDto.getTrade_status();
        log.info("收到支付结果：{}，支付记录：{}",payStatusDto.toString(),status);
        if(status.equals("TRADE_SUCCESS")){
            Float totalPrice = record.getTotalPrice() * 100;
            Float total_amount = Float.parseFloat(payStatusDto.getTotal_amount()) * 100;
            if(totalPrice.intValue() != total_amount.intValue()){
                //校验失败
                log.info("校验支付结果失败,支付记录:{},APP_ID:{},totalPrice:{}" ,record.toString(),payStatusDto.getApp_id(),total_amount.intValue());
                XueChengPlusException.cast("校验支付结果失败");
            }
            log.info("更新支付结果,支付交易流水号:{},支付结果:{}", payNo, status);
            record.setOutPayNo(payStatusDto.getTrade_no());
            record.setStatus("601002");
            record.setOutPayChannel("Alipay");
            record.setPaySuccessTime(LocalDateTime.now());
            boolean update = payRecordService.updateById(record);
            if(update){
                log.info("更新支付记录状态成功:{}", record.toString());
            }else{
                log.info("更新支付记录状态失败:{}", record.toString());
                XueChengPlusException.cast("更新支付记录状态失败");
            }
            //更新对应的订单信息
            Orders order = getById(record.getOrderId());
            if (order == null) {
                log.info("根据支付记录[{}}]找不到订单", record.toString());
                XueChengPlusException.cast("根据支付记录找不到订单");
            }
            order.setStatus("600002");
            boolean update1 = updateById(order);
            if (update1) {
                log.info("更新订单表状态成功,订单号:{}", order.getId());
            } else {
                log.info("更新订单表状态失败,订单号:{}", order.getId());
                XueChengPlusException.cast("更新订单表状态失败");
            }
            //保存消息记录,参数1：支付结果通知类型，2: 业务id，3:业务类型
            MqMessage message = mqMessageService.addMessage("payresult_notify",
                    order.getOutBusinessId().toString(),order.getOrderType(),null);
            //发送消息
            notifyPayResult(message);
        }
    }

    /**
     * 发送支付通知结果
     * @param message
     */
    @Override
    public void notifyPayResult(MqMessage message) {
        //1.消息体转json
        String msg = JSON.toJSONString(message);
        //设置消息持久化
        Message msgObj = MessageBuilder.withBody(msg.getBytes(StandardCharsets.UTF_8))
                .setDeliveryMode(MessageDeliveryMode.PERSISTENT)
                .build();
        //2.设置全局唯一的消息ID，需要封装到CorrelationData中
        CorrelationData correlationData = new CorrelationData(message.getId().toString());
        //3.添加callback
        correlationData.getFuture().addCallback(
                result -> {
                    if(result.isAck()){
                        //3.1. ack，消息成功发送到交换机
                        log.info("通知支付结果消息成功发送到交换机，ID:{}",correlationData.getId());
                        //删除消息表中的记录
                        mqMessageService.completed(message.getId());
                    }else{
                        //3.2. nack，消息从生产者到交换机的过程中发送失败
                        log.error("通知支付结果消息发送到交换机失败, ID:{}, 原因{}",
                                correlationData.getId(),result.getReason());
                    }
                },
                ex -> {
                    log.error("消息发送时出现异常, ID:{}, 原因{}",correlationData.getId(),ex.getMessage());
                }
        );
        //4.发送消息
        rabbitTemplate.send(PayNotifyConfig.PAYNOTIFY_EXCHANGE_FANOUT,"",
                msgObj,correlationData);
    }
}
